package qos.estimator.jdbc;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;

import qos.estimator.fuzzy.ClassificationTuple;
import qos.estimator.fuzzy.FuzzyEstimator;
import qos.estimator.statistics.SystemStatisticsCollector;
import qos.estimator.tpch.TPCHQueries;
import fuzzy.NoRulesFiredException;

/**
 * This class is responsible for intercepting an SQL statement and
 * forward it to the database.
 * 
 * @author lforleans
 *
 * TODO Predict query response time before query execution.
 * TODO Compare real execution time against predicted time. 
 *
 */
public class SerialQoSConnectionSPI {
	private ServerSocket serverSocket;
	private Map<Integer, String> tpchQueries = new HashMap<Integer, String>();
	private SystemStatisticsCollector sysStats;
	
	public SerialQoSConnectionSPI() {
		tpchQueries.put(0, TPCHQueries.q0);
		tpchQueries.put(1, TPCHQueries.q1);
		tpchQueries.put(2, TPCHQueries.q2);
		tpchQueries.put(3, TPCHQueries.q3);
		tpchQueries.put(4, TPCHQueries.q4);
		tpchQueries.put(5, TPCHQueries.q5);
		tpchQueries.put(6, TPCHQueries.q6);
		tpchQueries.put(7, TPCHQueries.q7);
		tpchQueries.put(8, TPCHQueries.q8);
		tpchQueries.put(9, TPCHQueries.q9);
		tpchQueries.put(10, TPCHQueries.q10);
		tpchQueries.put(11, TPCHQueries.q11);
		tpchQueries.put(12, TPCHQueries.q12);
		tpchQueries.put(13, TPCHQueries.q13);
		tpchQueries.put(14, TPCHQueries.q14);
		tpchQueries.put(15, TPCHQueries.q15);
		tpchQueries.put(16, TPCHQueries.q16);
		tpchQueries.put(17, TPCHQueries.q17);
		tpchQueries.put(18, TPCHQueries.q18);
		tpchQueries.put(19, TPCHQueries.q19);
		tpchQueries.put(20, TPCHQueries.q20);
		tpchQueries.put(21, TPCHQueries.q21);
		tpchQueries.put(22, TPCHQueries.q22);
	}

	protected Statement getStatement() {
		try {
			Class.forName("org.postgresql.Driver");
			Connection conn = DriverManager.getConnection(
					"jdbc:postgresql://localhost/tpch_sf10", "tpch", "tpch");
			return conn.createStatement();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

		return null;
	}
	
	public void startServer(boolean isCollectStatisticsMode){
		try {
			String fileName;
			if(isCollectStatisticsMode)
				fileName = "stats/qosdb.stats";
			else
				fileName = "stats/qosdb.results";
			sysStats = new SystemStatisticsCollector();
			new Thread(sysStats).start();
			FuzzyEstimator estimator = new FuzzyEstimator(true);
			FileWriter fw = new FileWriter(new File(fileName));
			PrintWriter pw = new PrintWriter(fw);
			serverSocket = new ServerSocket(8888);
			int i = 0;
			while((i++) < 35000){
				Socket clientSocket = serverSocket.accept();
				PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
				BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
				String sqlQuery;
				while ((sqlQuery = in.readLine()) != null) {
					int queryNumber = Integer.parseInt(sqlQuery);
					String query = tpchQueries.get(queryNumber);
					Statement stmt = getStatement();
					try {
						ClassificationTuple currentState = sysStats.getStatistics();
						long elapsedTime = System.currentTimeMillis();
						stmt.execute(query);
						elapsedTime = System.currentTimeMillis() - elapsedTime;
						String line = currentState.getAsLine()+"\t"+elapsedTime;
						try{
							String predictedElapsedTimeLabel = estimator.getExpectedElapsedTimeForQuery(currentState);
							String elapsedTimeLabel = estimator.getElapsedTimeLabel(elapsedTime);
							if(!isCollectStatisticsMode)
								line += "\t"+elapsedTimeLabel +"\t"+ predictedElapsedTimeLabel;
						}catch(NoRulesFiredException exception){
							//first call to getExpectedElasedTimeForQuery will throw
							//an exception here. Ignore.
						}finally{
							currentState.setElapsedTime(elapsedTime);
							System.out.print("Adding new rule to KB...");
							estimator.addRule(currentState);
							System.out.println(" done.");
						}
						
						pw.println(line);
						pw.flush();
						//System.out.println(line);
						stmt.close();
						stmt.getConnection().close();
						out.println("OK");

					} catch (SQLException e1) {
						e1.printStackTrace();
					}
				}
				out.close();
				in.close();
				clientSocket.close();
			}
			pw.close();
			fw.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new SerialQoSConnectionSPI().startServer(false);
	}

}
